home *** CD-ROM | disk | FTP | other *** search
- {***************************************************}
- { }
- { Turbo Pascal for Windows }
- { Windows 3.1 OLE Server Demonstration Program }
- { Server Object Unit }
- { }
- { Copyright (c) 1992 by Borland International }
- { }
- {***************************************************}
-
- { This unit defines the Server and Document objects, which
- represent the Ole Server and Ole Document, respectively.
- The Server interfaces with the Client application at the
- highest level, managing the creation and manipulation of
- Documents.
-
- Interaction between the Client and these objects is carried
- out through a series of callback functions, which are also
- defined here.
-
- NOTE that we only have one document per server. if yours
- was an MDI app, then you would have a list of documents.
-
- Note: To compile the OLE Server demo, set Compile|Primary File to OLESERVR.PAS
- }
-
- unit Server;
-
- interface
-
- uses WinTypes, CommDlg, Ole, WObjects, OleTypes, OleObj;
-
- type
-
- { The following record types represent the Server and Document
- objects within the OLE library. They are based on the
- standard structures defined in Ole.pas, and each adds one
- field to provide access back to the TPW object which represents
- it.
- }
- POleServerObj = ^TOleServerObj;
-
- PAppServer = ^TAppServer;
- TAppServer = record
- OleServer: TOleServer;
- Owner : POleServerObj;
- end;
-
- POleDocument = ^TOleDocument;
-
- PAppServerDoc = ^TAppServerDoc;
- TAppServerDoc = record
- OleServerDoc: TOleServerDoc;
- Owner : POleDocument;
- end;
-
- { TOleServerObj }
-
- { This object represents the OLE Server, wrapping useful
- behaviors around the basic TOleServer structure that is
- used within OLE to represent a Server. This structure
- is represented by the AppServer data field, which is of
- the TAppServer type defined in oleservr.pas, and which
- includes an additional field to point back to Self so
- that our callback functions can reference this object.
- }
- TOleServerObj = object(TObject)
- AppServer : TAppServer;
- ServerHdl : LHServer; { Registration handle returned
- by server library}
- Document : POleDocument;
- IsReleased: Boolean; { True if Release method has been called}
-
- constructor Init(App: PApplication; Embedded: Boolean);
- constructor InitFromFile(App: PApplication; Path: PChar);
-
- function Initialize(App: PApplication): Boolean;
-
- function RegisterWithDatabase: Boolean; virtual;
- function WantsToRegister: Boolean; virtual;
- end;
-
-
- { TOleDocument }
-
- { This object represents the OLE ServerDoc, wrapping useful
- behaviors around the basic TOleServerDoc structure that is
- used within OLE to represent a document. This structure
- is represented by the AppServerDoc data field, which is of
- the TAppServerDoc type defined in oleservr.pas, and which
- includes an additional field which points back to Self so
- that our callback functions can reference this object.
- }
- TOleDocument = object(TObject)
- AppServerDoc: TAppServerDoc;
- ServerDoc : LHServerDoc; { Registration handle returned by
- server library }
- DocType : TDocType;
- Name : PChar;
- OleObject : POleObjectObj;
- IsDirty : Boolean;
- IsReleased : Boolean; { True if Release method has been called }
-
- constructor Init(Server: POleServerObj; Doc: LHServerDoc;
- Path: PChar; Dirty: Boolean);
-
- procedure Setup(Path: PChar; MaxPathLen: Integer;
- var FNStruct: TOpenFileName); virtual;
- function LoadFromFile(Path: PChar): Boolean; virtual;
- procedure SaveDoc; virtual;
- procedure SaveAs; virtual;
- procedure Reset(Path: PChar); virtual;
- procedure SetDocumentName(NewName: PChar;
- ChangeCaption: Boolean); virtual;
- function PromptForOpenFileName(Path: PChar): Boolean; virtual;
- end;
-
- function TOleServerObj_InitVTbl(Inst: THandle): Boolean;
- function TOleDocument_InitVTbl(Inst: THandle): Boolean;
-
-
- implementation
-
- uses Strings, WinProcs, ServrWin, OleApp, ShellAPI;
-
- { Global variables }
-
- var
- OleServerVTbl : TOleServerVTbl;
- OleServerDocVTbl: TOleServerDocVTbl;
-
- Filter : array [0..100] of Char; { Used in Setup }
- SimpleName : array [0..13] of Char;
-
- const
- UnnamedDoc: PChar = '(Untitled)';
-
-
-
-
- { Server Callback Functions }
-
- { The first parameter to each callback is a pointer to the TOleServer
- structure that defines this document. In each case, we know that it
- will really be a pointer to a TAppServer record, which includes a
- pointer to the Pascal object which owns the TOleServer record. We
- can therefore use a typecast to access that object, and thus find our
- way back to Self.
- }
-
- { Handles the Open callback. The user has activated a linked object in an
- OLE client by calling OleActivate. Similar to CreateFromTemplate in that
- we need to create a document, initialize it with the contents of file
- 'DocName', and save the file name for later use.
-
- WHAT TO DO:
- - Create a TOleDocument of class 'ClassName' (since we only have one
- class we can ignore the class name)
- - Initialize the document with the contents of file 'DocName'
- - Associate handle 'Doc' with the document
- - Store the pointer to the TOleDocument in 'ServerDoc'
- - Save file name 'DocName'
- - Return ole_Ok if successful, ole_Error_Open otherwise
- }
- function Open(Server: POleServer; Doc: LHServerDoc; DocName: PChar;
- var ServerDoc: POleServerDoc): TOleStatus; export;
- var
- SelfPtr: POleServerObj;
- NewDoc : POleDocument;
- begin
- SelfPtr := PAppServer(Server)^.Owner;
-
- NewDoc := New(POleDocument, Init(SelfPtr, Doc, DocName, False));
- if NewDoc = nil then
- Open := ole_Error_Edit
- else
- begin
- ServerDoc := @NewDoc^.AppServerDoc;
- Open := ole_Ok;
- end;
- end;
-
- { Handles the Create callback. Called by the server library when a client
- application has created a new embedded object by calling OleCreate.
-
- WHAT TO DO:
- - Create an *untitled* TOleDocument of class 'ClassName' (since we
- only have one class we can ignore the class name) and mark it as dirty
- - Associate handle 'Doc' with the document
- - Store the pointer to the TOleDocument in 'ServerDoc'
- - Return ole_Ok if successful, ole_Error_New otherwise
-
- If your app is an MDI application then you would also allocate a window
- here, but since this app isn't the window is already created.
-
- 'DocName' is the name of the document as it appears in the client
- class. DON'T use this to change the title bar, use what you get when
- the document is sent the message 'SetHostNames'.
-
- NOTE: Since we only have one document we could have created it during
- initialization
- }
- function Create(Server: POleServer; Doc: LHServerDoc;
- Class, DocName: PChar;
- var ServerDoc: POleServerDoc): TOleStatus; export;
- var
- SelfPtr: POleServerObj;
- NewDoc : POleDocument;
- begin
- SelfPtr:= PAppServer(Server)^.Owner;
-
- NewDoc := New(POleDocument, Init(SelfPtr, Doc, nil, True));
- if NewDoc = nil then
- Create := ole_Error_New
- else
- begin
- ServerDoc := @NewDoc^.AppServerDoc;
- PServerWindow(Application^.MainWindow)^.BeginEmbedding;
- Create := ole_Ok;
- end;
- end;
-
- { Handles the CreateFromTemplate callback. Called by the server library
- when a client application has created a new linked object specifying a
- template by calling OleCreateFromTemplate. What this really means is that
- we need to create a document and initialize it with the contents of a file.
- 'DocName' is the name of the document as it appears in the client class.
- DON'T use this to change the title bar, use what you get when the document
- is sent message 'SetHostNames'
-
- WHAT TO DO:
- - Create a TOleDocument of class 'ClassName' (since we only have one
- class we can ignore the class name)
- - Initialize the document with the contents of file 'TemplateName'
- - Associate handle 'Doc' with the document
- - Store the pointer to the TOleDocument in 'ServerDoc'
- - Return ole_Ok if successful, ole_Error_Template otherwise
-
- If your app is an MDI application then you would also allocate a window
- here, but since this app isn't the window is already created.
-
- NOTE: since we only have one document we could have created it during
- initialization
- }
- function CreateFromTemplate(Server: POleServer; Doc: LHServerDoc;
- Class, DocName, TemplateName: PChar;
- var ServerDoc: POleServerDoc): TOleStatus; export;
- var
- SelfPtr: POleServerObj;
- NewDoc : POleDocument;
- begin
- SelfPtr:= PAppServer(Server)^.Owner;
-
- NewDoc := New(POleDocument, Init(SelfPtr, Doc, TemplateName, False));
- if NewDoc = nil then
- CreateFromTemplate := ole_Error_New
- else
- begin
- ServerDoc := @NewDoc^.AppServerDoc;
- PServerWindow(Application^.MainWindow)^.BeginEmbedding;
- CreateFromTemplate := ole_Ok;
- end
- end;
-
- { Handles the Edit callback. Called by the server library when a client
- application has activated an embedded object for editing. This is exactly
- like 'Create' except that the document will receive a 'GetData' message to
- create the object, and the object will receive a 'SetData' message to
- initialize itself
-
- 'DocName' is the name of the document as it appears in the client class.
- DON'T use this to change the title bar, use what you get when the document
- is sent message 'SetHostNames'
-
- WHAT TO DO:
- - Create a TOleDocument of class 'ClassName' (since we only have one
- class we can ignore the class name)
- - Associate handle 'Doc' with the document
- - Store the pointer to the TOleDocument in 'ServerDoc'
- - Return ole_Ok if successful, ole_Error_Edit otherwise
- }
- function Edit(Server: POleServer; Doc: LHServerDoc; Class, DocName: PChar;
- var ServerDoc: POleServerDoc): TOleStatus; export;
- var
- SelfPtr: POleServerObj;
- NewDoc : POleDocument;
- begin
- SelfPtr:= PAppServer(Server)^.Owner;
- NewDoc := New(POleDocument, Init(SelfPtr, Doc, nil, False));
- if NewDoc = nil then
- Edit := ole_Error_Edit
- else
- begin
- ServerDoc := @NewDoc^.AppServerDoc;
- PServerWindow(Application^.MainWindow)^.BeginEmbedding;
- Edit := ole_Ok;
- end;
- end;
-
- { Handles the Exit callback. We have been instructed by the library to
- exit immediately because of a fatal error.
-
- WHAT TO DO:
- - Hide the window to prevent user interaction
- - Call OleRevokeServer and ignore a return of ole_Wait_For_Release
- - Terminate the application immediately
- - Return ole_Ok if successful, ole_Error_Generic otherwise
- }
- function Exit(Server: POleServer): TOleStatus; export;
- var
- SelfPtr: POleServerObj;
- begin
- SelfPtr := PAppServer(Server)^.Owner;
-
- Application^.MainWindow^.Show(sw_Hide);
-
- OleRevokeServer(SelfPtr^.ServerHdl);
-
- PostAppMessage(GetCurrentTask, wm_Quit, 0, 0);
- Exit := ole_Ok;
- end;
-
- { Handles the Release callback. This routine gets called by the server
- library after the server has called OleRevokeServer and when the DDE
- conversation with the client has been successfully closed. This tells
- us that there are no connections to the server, its documents, or their
- objects and that we are free to terminate.
-
- WHAT TO DO:
- - Set a flag to indicate that 'Release' has been called
- - If the application is hidden and we *haven't* called OleRevokeServer
- then we *must* terminate by posting a wm_Close message
- - Free any resources allocated including documents, but *not* the
- TOleServer structure
- - Return ole_Ok if successful, Ole_Error_Generic otherwise
-
- NOTE: this routine is tricky because it is invoked under different
- circumstances:
- - User brought up the server and then closes it, which causes us
- to call OleRevokeServer which means the server will eventually
- receive a 'Release' message
-
- - The server was started to perform an invisible update for a client
- (i.e. the server has always been hidden). In this case the server will
- receive a 'Release' message and we must tell ourselves to close
- because there is no user interaction.
- }
- function Release(Server: POleServer): TOleStatus; export;
- var
- SelfPtr: POleServerObj;
- begin
- SelfPtr := PAppServer(Server)^.Owner;
-
- { If we haven't been sent a 'Release' message yet and our main window is
- hidden then we post a quit message. NOTE: Call PostMessage and not
- PostQuitMessage because PostQuitMessage might bypass your application's
- necessary cleanup procedures.
- }
- if (not SelfPtr^.IsReleased) and
- (not IsWindowVisible(Application^.MainWindow^.HWindow)) then
- PostMessage(Application^.MainWindow^.HWindow, wm_Close, 0, 0);
-
- SelfPtr^.IsReleased := True;
-
- Release := ole_Ok;
- end;
-
- { Handles the Execute callback. If your app supports DDE execution
- commands then you would handle this event. Since we don't we return
- ole_Error_Command.
- }
- function Execute(Server: POleServer; Commands: THandle): TOleStatus; export;
- begin
- Execute := ole_Error_Command;
- end;
-
-
- { TOleServerObj Methods }
-
- { Constructs an untitled instance of the OLE server document.
- }
- constructor TOleServerObj.Init(App: PApplication; Embedded: Boolean);
- begin
- if Initialize(App) and (not Embedded) then
- Document := New(POleDocument, Init(@Self, 0, nil, False));
- end;
-
- { Constructs an instance of the Server Object, creating an OLE document
- and initializing it from file 'Path'.
- }
- constructor TOleServerObj.InitFromFile(App: PApplication; Path: PChar);
- begin
- if Initialize(App) then
- Document := New(POleDocument, Init(@Self, 0, Path, False));
- end;
-
- { Completes the construction of Self, attaching Self to the given
- application. Returns True if successful, False if not.
- }
- function TOleServerObj.Initialize(App: PApplication): Boolean;
- var
- Status: TOleStatus;
- begin
- AppServer.OleServer.lpvtbl:= @OleServerVTbl;
- AppServer.Owner := @Self;
-
- IsReleased := False;
-
- { Attach Self to the containing application.
- }
- POleApp(App)^.Server := @Self;
-
- { Since we can't handle multiple documents (MDI), request that we use
- multiple instances to support multiple objects
- }
- Status := OleRegisterServer(ClassKey, @AppServer, ServerHdl, HInstance,
- ole_Server_Multi);
-
- Initialize := True;
- if Status = ole_Error_Class then
- begin
- if RegisterWithDatabase then
- OleRegisterServer(ClassKey, @AppServer, ServerHdl, HInstance,
- ole_Server_Multi)
- else
- Initialize := False;
- end;
- end;
-
- { Displays an action message prompting the user to see if they want to
- register Application^.Name with the system registration database.
- Returns True if user says YES and False is users says NO. If user
- says NO we terminate the app.
- }
- function TOleServerObj.WantsToRegister: Boolean;
- var
- Buf: array [0..255] of Char;
- begin
- StrCopy(Buf, Application^.Name);
- StrCat(Buf, ' is not registered as an OLE server in the ' +
- 'system registration');
- StrCat(Buf, ' database. Do you want to register it?');
-
- if MessageBox(0, Buf, Application^.Name, mb_YesNo or
- mb_IconQuestion) = idYes then
- WantsToRegister := True
- else
- begin
- PostAppMessage(GetCurrentTask, wm_Quit, 0, 0);
-
- { We also need to make sure that the main window doesn't get displayed.
- We have a couple of choices: set 'CmdShow' to sw_Hide or set 'Status'
- to non-zero. Since the user electing not to register isn't really an
- error, let's set 'CmdShow'.
- }
- CmdShow := sw_Hide;
- WantsToRegister := False;
- end;
- end;
-
- { Registers us as an OLE server with the system registration database.
- This would typically be done during *installation* of the app and not
- when the app runs.
-
- NOTE: We first prompt the user to see if they want us to register. if so
- we register and if not we terminate the app.
- }
- function TOleServerObj.RegisterWithDatabase: Boolean;
- var
- Buf : array [0..127] of Char;
- Path : array [0..255] of Char;
- begin
- if not WantsToRegister then
- RegisterWithDatabase := False
- else
- begin
- StrCopy(Buf, '.');
- StrCat(Buf, FileExt);
- RegSetValue(hkey_Classes_Root, Buf, Reg_Sz, ClassKey, StrLen(ClassKey));
- RegSetValue(hkey_Classes_Root, ClassKey, Reg_Sz, ClassValue,
- StrLen(ClassValue));
-
- { Register verb actions EDIT and PLAY with EDIT being the primary verb.
- }
- StrCopy(Buf, ClassKey);
- StrCat(Buf, '\protocol\StdFileEditing\verb\0');
- RegSetValue(hkey_Classes_Root, Buf, Reg_Sz, 'Edit', 4);
-
- StrCopy(Buf, ClassKey);
- StrCat(Buf, '\protocol\StdFileEditing\verb\1');
- RegSetValue(hkey_Classes_Root, Buf, Reg_Sz, 'Play', 4);
-
- { Register a full pathname to the executable with the database.
- }
- GetModuleFileName(HInstance, Path, SizeOf(Path));
- StrCopy(Buf, ClassKey);
- StrCat(Buf, '\protocol\StdFileEditing\server');
- RegSetValue(hkey_Classes_Root, Buf, Reg_Sz, Path, StrLen(Path));
-
- { Inform the user that we have registered as an OLE server by displaying
- an information message.
- }
- StrCopy(Buf, Application^.Name);
- StrCat(Buf, ' successfully registered as an OLE server with the system '+
- 'registration database.');
-
- MessageBox(0, Buf, Application^.Name, mb_Ok or mb_IconInformation);
- RegisterWithDatabase := True;
- end
- end;
-
- { Creates the instance thunks for the OleServer callback tables.
- }
- function TOleServerObj_InitVTbl(Inst: THandle): Boolean;
- begin
- @OleServerVTbl.Open := MakeProcInstance(@Open, Inst);
- @OleServerVTbl.Create := MakeProcInstance(@Create, Inst);
- @OleServerVTbl.CreateFromTemplate
- := MakeProcInstance(@CreateFromTemplate, Inst);
- @OleServerVTbl.Edit := MakeProcInstance(@Edit, Inst);
- @OleServerVTbl.Exit := MakeProcInstance(@Exit, Inst);
- @OleServerVTbl.Release := MakeProcInstance(@Release, Inst);
- @OleServerVTbl.Execute := MakeProcInstance(@Execute, Inst);
-
- TOleServerObj_InitVTbl := (@OleServerVTbl.Open <> nil) and
- (@OleServerVTbl.Create <> nil) and
- (@OleServerVTbl.CreateFromTemplate <> nil) and
- (@OleServerVTbl.Edit <> nil) and
- (@OleServerVTbl.Exit <> nil) and
- (@OleServerVTbl.Release <> nil) and
- (@OleServerVTbl.Execute <> nil);
- end;
-
-
- { Document Callback Functions }
-
- { The first parameter to each callback is a pointer to the TOleServerDoc
- structure that defines this document. In each case, we know that it
- will really be a pointer to a TAppServerDoc record, which includes a
- pointer to the Pascal object which owns the TOleServerDoc record. We
- can therefore use a typecast to access that object, and thus find our
- way back to Self.
- }
-
- { Handles the Save callback. This method is only used when the server is
- editing a linked object: the client application is closing and the user
- has requested saving the client document which contains a linked object.
-
- WHAT TO DO:
- - Save the document to the filename which was passed in when the document
- was opened for linking
- - Return Ole_Ok if successful, ole_Error_Generic otherwise
- }
- function Save(Doc: POleServerDoc): TOleStatus; export;
- var
- SelfPtr: POleDocument;
- begin
- SelfPtr := PAppServerDoc(Doc)^.Owner;
-
- if SelfPtr^.DocType <> DoctypeFromFile then
- Save := Ole_Error_Generic
- else
- begin
- SelfPtr^.SaveDoc;
- Save := Ole_Ok;
- end;
- end;
-
- { Handles the Close callback. We have been requested to close the document
- because the client that contains a link (embedding or linking) to that
- document has shut down. This is always called *before* the document's
- 'Release' callback is called.
-
- WHAT TO DO:
- - Call OleRevokeServerDoc and *don't* free any resources until
- 'Release' is called
- - Return the value of OleRevokeServerDoc
- }
- function Close(Doc: POleServerDoc): TOleStatus; export;
- var
- SelfPtr: POleDocument;
- begin
- SelfPtr:= PAppServerDoc(Doc)^.Owner;
-
- Close := OleRevokeServerDoc(SelfPtr^.ServerDoc);
- end;
-
- { Responds to the SetHostNames callback. The server library is calling
- to provide the server with the name of the client's document and the
- name of the object in the client application. These names should be
- used to make the necessary window title bar and menu changes.
-
- This is only called for embedded objects because linked objects display
- their filename in the title bar.
-
- WHAT IT DOES:
- - Change the title bar and File menu
- - Store the object and client names for later use
- - Return Ole_Ok is successful, Ole_Error_Generic otherwise
-
- PARAMETERS:
- - 'Client' is the name of the client application document
- - 'Doc' is the name of the object in the client application
- }
- function SetHostNames(Doc: POleServerDoc; Client,
- DocName: PChar): TOleStatus; export;
- var
- SelfPtr: POleDocument;
- Title : array [0..63] of Char;
- begin
- SelfPtr := PAppServerDoc(Doc)^.Owner;
- PServerWindow(Application^.MainWindow)^.UpdateFileMenu(DocName);
-
- { Store the document name, but don't update the title bar; we will do that
- below
- }
- SelfPtr^.SetDocumentName(DocName, True);
-
- { Set the caption to be <App Name> - <Object Name> in <Client App Document>
- }
- StrCopy(Title, Application^.Name);
- StrCat (Title, ' - ');
- StrCat (Title, DocName);
- StrCat (Title, ' in ');
- StrCat (Title, Client);
- PWindow(Application^.MainWindow)^.SetCaption(Title);
-
- SetHostNames := Ole_Ok;
- end;
-
- { Handles the DocSetDimensions callback. The client is informing us how
- big the object should be. 'Rect' is in mm_HiMetric units (all OLE
- libraries express the size of every object in mm_HiMetric). This
- function is not supported.
- }
- function SetDocDimensions(Doc: POleServerDoc;
- var Bounds: TRect): TOleStatus; export;
- begin
- SetDocDimensions := Ole_Ok;
- end;
-
- { Handles the GetObject callback. The server library calls this method
- whenever a client application creates an object using a function like
- OleCreate. If 'ObjName' is nil, that means we are being called for an
- embedded object after the server was sent 'Create', 'Edit', or
- 'CreateFromTemplate' and the server library requests the entire document.
-
- If 'ObjName' isn't nil then the server has already received a 'Open'
- message to activate the linked object
-
- WHAT TO DO:
- - Allocate a TOleObject if 'Item' is nil, or look up 'Item'
- in the list of objects if it isn't nil
- - Store the pointer to the TOleObject in 'OleObject' for return
- - Store 'Client' so we can send notifications to the client
- (used for linked objects)
- - Return ole_Ok if successful, ole_Error_Name if 'Item' isn't
- recognized, or ole_Error_Memory if the object could not be
- allocated
-
- NOTE:
- - We only have one object and it is created when the document is
- created. Therefore, we don't actually create anything here.
- - 'Client' resides in the server library and is used on behalf of
- a client application
- }
- function GetObject(Doc: POleServerDoc; Item: PChar;
- var OleObject: POleObject; Client: POleClient): TOleStatus; export;
- var
- SelfPtr: POleDocument;
- begin
- SelfPtr := PAppServerDoc(Doc)^.Owner;
-
- { In either case (whether 'ObjName' is nil or not) we just return
- the object associated with the document. NOTE that we return a
- pointer to its AppObject field, not to the object itself.
- }
- OleObject := POleObject(@SelfPtr^.OleObject^.AppObject);
-
- { If 'Item' isn't nil then we associate 'Client' with it.
-
- NOTE: We only have one object. if you have multiple objects then you
- would have to search your objects to find the one that matched
- 'Item'
- }
- if Item <> nil then
- SelfPtr^.OleObject^.AddClientLink(Client);
-
- GetObject := Ole_Ok;
- end;
-
- { Handles the Release callback. The server library calls this routine when
- all conversations to the object have been closed. At this point the server
- has called either OleRevokeServerDoc or OleRevokeServer.
-
- There will be no more calls to the document's methods. It is thus okay to
- free the document's objects, but *not* the TOleDocument yet.
-
- WHAT TO DO:
- - Free the document's objects and resources (e.g. atoms) but *not* the
- document itself
- - Set a flag to indicate that 'Release' has been called
- - Return Ole_Ok if successful, Ole_Error_Generic otherwise
-
- NOTE:
- - Since we only have one document and one object within the
- document we don't delete the object here. However, you
- might want to.
- - This procedure is not called 'Release' because it appears in the
- same scope as the Release callback for the TOleServerObj.
- }
- function ReleaseDoc(Doc: POleServerDoc): TOleStatus; export;
- var
- SelfPtr: POleDocument;
- begin
- SelfPtr := PAppServerDoc(Doc)^.Owner;
-
- SelfPtr^.IsReleased := True;
- ReleaseDoc := Ole_Ok;
- end;
-
- { Handles the SetColorScheme callback. Not supported.
- }
- function SetColorSchemeDoc(Doc: POleServerDoc; var Palette: TLogPalette): TOleStatus; export;
- begin
- SetColorSchemeDoc := Ole_Error_Generic;
- end;
-
- { Handles the Execute callback. If your app supports DDE execution commands
- then you would handle this event. Since we don't, we return
- Ole_Error_Command.
- }
- function ExecuteDoc(Doc: POleServerDoc;
- Commands: THandle): TOleStatus; export;
- begin
- ExecuteDoc := ole_Error_Command;
- end;
-
-
- { TOleDocument Methods }
-
- { Constructs an instance of the OLE Document. If 'Path' is nil then we
- create an untitled document and default object. The type is 'DoctypeNew'
- if 'ServerDoc' is nil and 'DoctypeEmbedded' if 'ServerDoc' is non-nil.
- If 'Path' is non-nil we create a document of type 'DoctypeFromFile'
- and initialize it from file 'Path'
-
- If 'ServerDoc' is nil then we call OleRegisterServerDoc, otherwise we
- just use 'ServerDoc' as our registration handle.
- }
- constructor TOleDocument.Init(Server: POleServerObj; Doc: LHServerDoc;
- Path: PChar; Dirty: Boolean);
- begin
- Name := nil;
- IsReleased:= False;
- IsDirty := Dirty;
-
- AppServerDoc.OleServerDoc.lpvtbl:= @OleServerDocVTbl;
- AppServerDoc.Owner := @Self;
-
- { Attach this document to the owning server.
- }
- POleServerObj(Server)^.Document := @Self;
-
- { Since we only have one object we can create it now.
- }
- OleObject := New(POleObjectObj, Init);
-
- if Path <> nil then
- LoadFromFile(Path)
- else
- begin
- SetDocumentName(UnnamedDoc, True);
-
- if Doc <> 0 then
- DocType := DoctypeEmbedded
- else
- DocType := DoctypeNew;
- end;
-
- if Doc <> 0 then
- ServerDoc := Doc { Use registration handle we were given }
- else
- OleRegisterServerDoc(Server^.ServerHdl, Name, @AppServerDoc, ServerDoc);
- end;
-
- { Changes the instance variable 'Name' and changes the window caption to
- those given.
- }
- procedure TOleDocument.SetDocumentName(NewName: PChar;
- ChangeCaption: Boolean);
- var
- Title: array[0..63] of Char;
- begin
- StrDispose(Name);
- Name := StrNew(NewName);
-
- if ChangeCaption then
- begin
- StrCopy(Title, Application^.Name);
- StrCat (Title, ' - ');
- StrCat (Title, NewName);
- PWindow(Application^.MainWindow)^.SetCaption(Title);
- end;
- end;
-
- { Loads from the given file name. Returns True if successful and False
- otherwise. If successful sets DocType to 'DoctypeFromFile' and sets
- 'Name' to 'Path'.
- }
- function TOleDocument.LoadFromFile(Path: PChar): Boolean;
- var
- Msg : array [0..255] of Char;
- Key : array [0..40] of Char;
- InStream: TBufStream;
- begin
- InStream.Init(Path, stOpen, 1000);
- if InStream.Status = stInitError then
- begin
- StrCopy(Msg, 'Cannot open file ');
- StrCat(Msg, Path);
- MessageBeep(0);
- MessageBox(Application^.MainWindow^.HWindow, Msg,
- Application^.Name, mb_OK or mb_IconExclamation);
- LoadFromFile := False;
- end
- else
- begin
- { Read in the signature. Read the number of characters we
- would expect, then see if we got them. If not, then abandon
- the attempt. Note that the Read will not get in a NUL; we
- put that on manually. Also note that we read StrLen(ClassKey)+1
- characters to consume the extra blank written out.
- }
- InStream.Read(Key, StrLen(ClassKey)+1);
- Key[StrLen(ClassKey)] := #0;
- if StrComp(Key, ClassKey) <> 0 then
- begin
- StrCopy(Msg, 'File ');
- StrCat(Msg, Path);
- StrCat(Msg, ' is not an "');
- StrCat(Msg, Application^.Name);
- StrCat(Msg, '" file!');
- MessageBeep(0);
- MessageBox(Application^.MainWindow^.HWindow, Msg, Application^.Name,
- mb_OK or mb_IconExclamation);
- LoadFromFile := False;
- end
- else
- begin
- OleObject:= POleObjectObj(InStream.Get);
- DocType := DoctypeFromFile;
- SetDocumentName(Path, True);
- LoadFromFile := True;
- end;
- end;
- InStream.Done;
- end;
-
- { Resets the document so that we can re-use the document object. If your
- app doesn't then you would delete the old object and create a new one.
- Sets 'IsDirty' flag to False and 'IsReleased' to False. If 'ServerDoc'
- is nil then calls OleRegisterServerDoc.
- }
- procedure TOleDocument.Reset(Path: PChar);
- begin
- IsDirty := False;
- IsReleased := False;
-
- if Path <> nil then
- if not LoadFromFile(Path) then
- begin
- PServerWindow(Application^.MainWindow)^.ShapeChange(ObjEllipse);
-
- OleObject^.Native.NativeType := ObjEllipse;
- OleObject^.Native.Version := 1;
-
- DocType := DoctypeNew;
- SetDocumentName(UnnamedDoc, True);
- end;
-
- if ServerDoc = 0 then
- OleRegisterServerDoc(POleApp(Application)^.Server^.ServerHdl, Name,
- @AppServerDoc, ServerDoc);
- end;
-
- { Sets up a TOpenFileName structure for use with the File Open Common
- Dialog. The caller passes in a structure which is filled in as
- required, and a pointer to the array to receive the full path name.
- Uses the Filter and SimpleName variables defined above, which are
- global to allow this to be used from several places.
- }
- procedure TOleDocument.Setup(Path: PChar; MaxPathLen: Integer;
- var FNStruct: TOpenFileName);
- begin
- { Set up a filter buffer to look for '*.oos' files only. Recall that filter
- buffer is a set of string pairs, with the last one terminated by a
- double-null.
- }
- FillChar(Filter, SizeOf(Filter), #0); { Set up for double null at end }
- StrCopy(Filter, 'OWL OLE Server');
- StrCopy(@Filter[StrLen(Filter)+1], '*.oos');
-
- StrCopy(Path, '*.');
- StrCat (Path, FileExt);
-
- FillChar(FNStruct, SizeOf(TOpenFileName), #0);
-
- with FNStruct do
- begin
- hInstance := HInstance;
- hwndOwner := Application^.MainWindow^.HWindow;
- lpstrDefExt := FileExt;
- lpstrFile := Path;
- lpstrFilter := Filter;
- lpstrFileTitle:= SimpleName;
- Flags := ofn_HideReadOnly or ofn_PathMustExist;
- lStructSize := SizeOf(TOpenFileName);
- nFilterIndex := 1; {Use first Filter String in lpstrFilter}
- nMaxFile := MaxPathLen;
- end;
- end;
-
- { Activates the File/Open common dialog, and returns the result.
- Puts the obtained file name into the given Path parameter, which
- is assumed to point to a buffer big enough to contain a TFilename
- sized string.
- }
- function TOleDocument.PromptForOpenFileName(Path: PChar): Boolean;
- var
- FNStruct: TOpenFileName;
- begin
- Setup(Path, SizeOf(TFilename), FNStruct);
- PromptForOpenFileName := GetOpenFileName(FNStruct);
- end;
-
- { Calls the common Windows dialog function to prompt the user for the
- filename to use.
- }
- procedure TOleDocument.SaveAs;
- var
- Path : TFilename; { Result of GetSaveFileName }
- FNStruct: TOpenFileName;
- begin
- Setup(Path, SizeOf(Path), FNStruct);
-
- if GetSaveFileName(FNStruct) then
- begin
- DocType := DoctypeFromFile;
- SetDocumentName(Path, True); { We must do this BEFORE we call SaveDoc }
- SaveDoc;
-
- { Now inform the server library that we have renamed the document
- }
- OleRenameServerDoc(ServerDoc, Name);
- end;
- end;
-
- { Saves the document to file 'Name' and marks the document as no
- longer 'dirty'.
- }
- procedure TOleDocument.SaveDoc;
- var
- OutStream: TBufStream;
- Blank : Char;
- begin
- if DocType = DoctypeNew then
- SaveAs
- else
- begin
- OutStream.Init(Name, stCreate, 1000);
- OutStream.Write(ClassKey^, StrLen(ClassKey));
- Blank := ' ';
- OutStream.Write(Blank, 1);
- OutStream.Put(OleObject);
- IsDirty := False;
- OutStream.Done;
- end;
- end;
-
- { Creates thunks for TOleServerDoc method callback tables
- }
- function TOleDocument_InitVTbl(Inst: THandle): Boolean;
- begin
- @OleServerDocVTbl.Save := MakeProcInstance(@Save, Inst);
- @OleServerDocVTbl.Close := MakeProcInstance(@Close, Inst);
- @OleServerDocVTbl.SetHostNames := MakeProcInstance(@SetHostNames, Inst);
- @OleServerDocVTbl.SetDocDimensions:= MakeProcInstance(@SetDocDimensions, Inst);
- @OleServerDocVTbl.GetObject := MakeProcInstance(@GetObject, Inst);
- @OleServerDocVTbl.Release := MakeProcInstance(@ReleaseDoc, Inst);
- @OleServerDocVTbl.SetColorScheme := MakeProcInstance(@SetColorSchemeDoc, Inst);
- @OleServerDocVTbl.Execute := MakeProcInstance(@ExecuteDoc, Inst);
-
- TOleDocument_InitVTbl := (@OleServerDocVTbl.Save <> nil) and
- (@OleServerDocVTbl.Close <> nil) and
- (@OleServerDocVTbl.SetHostNames <> nil) and
- (@OleServerDocVTbl.SetDocDimensions <> nil) and
- (@OleServerDocVTbl.GetObject <> nil) and
- (@OleServerDocVTbl.Release <> nil) and
- (@OleServerDocVTbl.SetColorScheme <> nil) and
- (@OleServerDocVTbl.Execute <> nil);
- end;
-
- end.
-